
/**
  swiss-army-knife.c

  @author Matthew Adam Poage
  @author list primary authors of each component
  @version February 18, 2009, 9:02:28 AM
*/

#include <avr/interrupt.h>
#include <inttypes.h>
#include <avr/io.h>
#include <avr/sleep.h>        // definitions for power-down modes
#include <math.h>
#include <avr/eeprom.h>
#include <avr/pgmspace.h>     // definitions for keeping constants in program memory

#define F_CPU 8000000UL       // sets up the default speed for delay.h
#include <util/delay.h>

#include <functions.h>
#include <utilities.h>

/* attiny84
  +-----------------------------------|  |-----------------------------------+
  |  1 VCC                                                            GND 14 |
  |  2 PB0 (PCINT8/XTAL1/CLKI)                     (ADC0/AREF/PCINT0) PA0 13 |
  |  3 PB1 (PCINT9/XTAL2)                          (ADC1/AIN0/PCINT1) PA1 12 |
  |  4 PB3 (PCINT11/RESET/dW)                      (ADC2/AIN1/PCINT2) PA2 11 |
  |  5 PB2 (PCINT10/INT0/OC0A/CKOUT)                 (ADC3/T0/PCINT3) PA3 10 |
  |  6 PA7 (PCINT7/ICP/OC0B/ADC7)           (ADC4/USCK/SCL/T1/PCINT4) PA4  9 |
  |  7 PA6 (PCINT6/OC1A/SDA/MOSI/DI/ADC6)  (ADC5/DO/MISO/OC1B/PCINT5) PA5  8 |
  +--------------------------------------------------------------------------+
*/

/*
void delayTwoCycles();
void delay_ms(uint16_t ms);

void delayuss(unsigned int micros);
void delayus();
void delayms(uint16_t millis);
inline unsigned long random(void);

void delay_one_tenth_ms(unsigned long int ms);
void blink_LEDs(unsigned long int duration, unsigned long int onTime,
  unsigned long int offTime);
*/

/*
void backAndForth();
void candle();
void flashlight();
void noisetoy();
  void noise();
  void dave_matthews_band();
void slm();
*/

/*
#define HEX__(n) 0x##n##UL
#define B8__(x) ((x&0x0000000FLU)?1:0)  \
               +((x&0x000000F0LU)?2:0)  \
               +((x&0x00000F00LU)?4:0)  \
               +((x&0x0000F000LU)?8:0)  \
               +((x&0x000F0000LU)?16:0) \
               +((x&0x00F00000LU)?32:0) \
               +((x&0x0F000000LU)?64:0) \
               +((x&0xF0000000LU)?128:0)
#define B8(d) ((unsigned char)B8__(HEX__(d)))
#define NUM_ELEM(x) (sizeof (x) / sizeof (*(x)))

#define false 0
#define true 1
*/

void readEEPROM();
void writeEEPROMCurrentProgram();
void writeEEPROMCurrentStates();
  uint8_t EEMEM eepromProgram;
  uint8_t EEMEM eepromStates[6];

void backAndForth();
void candle();
void flashlight();
void noisetoy();
  void noise();
  void daveMatthewsBand();
void slm();
void miniPOV();
void menorah();

inline unsigned long random(void);
void lightOn(volatile uint8_t *port, uint8_t mask);
void lightOff(volatile uint8_t *port, uint8_t mask);

static volatile uint8_t program = 0, state = 0;
static volatile uint8_t programShouldStopRunning = false;
static volatile uint8_t numberOfPrograms = 7, numberOfStates = 8;
static volatile uint8_t states[7];

// this sets the order of the lights on the breadboard and tin. the pin configuration
// on the attiny84 is not as nice as that on the attiny2313.

uint8_t numberOfLights = 8;
uint8_t lights[][2] = { // sequence of lights on breadboard
  { &PORTA, PA5 }, { &PORTA, PA4 }, { &PORTA, PA3 }, { &PORTA, PA2 }, { &PORTA, PA1 },
  { &PORTA, PA0 }, { &PORTB, PB0 }, { &PORTB, PB1 } };  // { &PORTB, PB3 (RESET) }

  // uint8_t lights[][2] = { // sequence of lights on PCB
  //   { &PORTA, PA2 }, { &PORTA, PA1 }, { &PORTA, PA0 }, { &PORTA, PA3 }, { &PORTA, PA4 },
  //   { &PORTA, PA5 }, { &PORTB, PB0 }, { &PORTB, PB1 } };  // { &PORTB, PB3 (RESET) }

uint8_t currentPin = 0x00, delayDuration, PB;
long randomValue;

// noisetoy -- noise()

/*
unsigned int  pitch    = 200;
unsigned char count    =  20;
unsigned char maxcount =  20;
unsigned int  sub      =  50;
unsigned char buttonCount = 0;
*/

// noisetoy -- dave_matthews_band()

int counter = 0;
int counter4 = 0;
int counter2 = 500;
int counter3 = 500;
int direction = -1;
int LUpperLim = 8000;
int RUpperLim = 8000;
int LReset = 500;
int RReset = 500;

SIGNAL(PCINT0_vect) {
  delay_ms(50);

  if (PINA & _BV(PA7)) {            // PCINT7 PA7 6 is one/high still down
    delay_ms(250);
    if (!(PINA & _BV(PA7))) {       // PCINT7 PA7 6 is zero/low released
      state++; state %= numberOfStates;
      states[program] = state;

      writeEEPROMCurrentStates();
    }
    else {                          // PCINT7 PA7 6 is one/high still down
      program++;
      program %= numberOfPrograms;
      state = states[program];

      writeEEPROMCurrentProgram();

      // when the program changes, set the programShouldStopRunning flag to
      // signal that a program should stop running. not all programs make use
      // of this flag.

      programShouldStopRunning = true;
    }
  }
}

int main (void) {
  // read the saved program and set of states from EEPROM

  readEEPROM();

  // set appropriate pins for use as led output

  DDRA  = 1 << PA7 | 1 << PA5 | 1 << PA4 | 1 << PA3 | 1 << PA2 | 1 << PA1 | 1 << PA0;
  DDRB  = 1 << PB1 | 1 << PB0;  // 1 << PB3 (RESET)

  // PB3 is the RESET pin. it can be configured as an output pin (which is necessary
  // to get nine lights for the menorah) by setting appropriate fuses. however,
  // high voltage programming is then needed to program the chip. the USBtiny
  // programmer cannot program a chip whose RESET has been disabled :-(

  // these two pins are the audio output pins. they should not be set to output
  // since they might pick up interference until they are actually being used
  // to create sound. functions that use them should set and reset them in a
  // responsible fashion.

  // DDRA |= 1 << PA6;
  // DDRB |= 1 << PB2;

  // tell pin change mask PCMSK0 to listen to PCINT7 6. the pin is normally held
  // low and a pushbutton switch pulls it high. note -- PCINT7 PA7 6 needs to be
  // normally low. avoid setting PORTA = 0xff or generally manipulating the entire
  // port indiscriminately.

  PCMSK0 |= 1 << PCINT7;  // tell pin change mask to listen to PCINT7 6
  GIMSK  |= 1 << PCIE0;   // enable PCINT interrupt in the general interrupt mask

  // enable interupts

  sei();

  while(true) {
    // programShouldStopRunning is a flag to indicate that a program should wrap
    // up what it's doing and let the next one take over. in several cases this
    // stops a long loop or turns off the sound (in slm()). not all functions make
    // use of the programShouldStopRunning flag.

    switch (program) {
      case 0: backAndForth(); programShouldStopRunning = false; break;
      case 1: candle();       programShouldStopRunning = false; break;
      case 2: flashlight();   programShouldStopRunning = false; break;
      case 3: noisetoy();     programShouldStopRunning = false; break;
      case 4: slm();          programShouldStopRunning = false; break;
      case 5: miniPOV();      programShouldStopRunning = false; break;
      case 6: menorah();
    }
  }

  return true;
}

//''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
// EEPROM functions
//   readEEPROM()
//   writeEEPROMCurrentProgram()
//   writeEEPROMCurrentStates()
//

void readEEPROM() {
  if (eeprom_read_byte(&eepromProgram) > numberOfPrograms) program = 0U;
  else program = (uint8_t)(eeprom_read_byte(&eepromProgram));

  // read the currentProgram from eeprom. if the value is out of range, set it to zero.
  // read the block of states from eeprom. if any of the values is greater than the
  // number of states, set the state to zero.

  eeprom_read_block((void*)&states, (const void*)&eepromStates, (uint8_t)numberOfPrograms);

  int i;
  for (i = 0; i < numberOfPrograms; i++) {
    if (states[i] > numberOfStates) states[i] = 0U;
  }

  state = states[program];
}

void writeEEPROMCurrentProgram() {
  eeprom_write_byte(&eepromProgram, (uint8_t)program);
}

void writeEEPROMCurrentStates() {
  eeprom_write_block((const void*)&states,
    (void*)&eepromStates, (uint8_t)numberOfPrograms);
}

//
//''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

//''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
// utility functions
//

unsigned long nextRandom = 1;
inline unsigned long random(void) {
  nextRandom = nextRandom * 1103515245 + 12345;
  return ((nextRandom >> 16) & 32767);
}

void lightOn(volatile uint8_t *port, uint8_t mask) {
  *port |= _BV(mask);
}

void lightOff(volatile uint8_t *port, uint8_t mask) {
  *port &= ~_BV(mask);
}

//
//''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

//''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
// backAndForth()
//

void backAndForth() {
  delayDuration = 20 * state;

  // could be improved by have a single global variable that tracks the
  // light that's on.

  // this could be improved by figuring out how to keep lastLight one behind i
  // and avoid calculating lastLight each time. this could be done by adding a
  // dummy light in position 0 to lights and using lights 1..8 instead of 0..7

  // the last light on the return trip is not being turned off properly

  int i, lastLight;
  for (i = 0; i < numberOfLights; i++) {
    lastLight = (i + numberOfLights - 1) % numberOfLights;

    lightOff(lights[lastLight][0], lights[lastLight][1]);
    lightOn(lights[i][0], lights[i][1]);

    delay_ms(delayDuration);
  }

  for (i = numberOfLights - 2; i >= 1; i--) {
    lastLight = (i + 1) % numberOfLights;

    lightOff(lights[lastLight][0], lights[lastLight][1]);
    lightOn(lights[i][0], lights[i][1]);

    delay_ms(delayDuration);
  }
}

//
//''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

//''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
// candle()
//

void candle() {
  randomValue = random();
  currentPin = randomValue % 8;

  randomValue = random();
  if ((randomValue % (state+2)) == 0) {
    lightOn(lights[currentPin][0], lights[currentPin][1]);
  }
  else {
    lightOff(lights[currentPin][0], lights[currentPin][1]);
  }
  delay_ms(5);
}

//
//''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

//''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
// flashlight()
//

void flashlight() {
  int i;
  delayDuration = state;

  if (delayDuration == 0) {
    for (i = 0; i < numberOfLights; i++) {
      lightOff(lights[i][0], lights[i][1]);
    }

    return;
  }

  for (i = 0; i < numberOfLights; i++) {
    lightOn(lights[i][0], lights[i][1]);
  }
  delay_ms(delayDuration);

  for (i = 0; i < numberOfLights; i++) {
    lightOff(lights[i][0], lights[i][1]);
  }

  delay_ms(10 - delayDuration);
}

//
//''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

//''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
// noisetoy()
//   noise()
//   daveMatthewsBand()
//

void noisetoy() {
  noise();
}

unsigned int  pitch    = 200;
unsigned char count    =  20;
unsigned char maxcount =  20;
unsigned int  sub      =  50;
unsigned char buttonCount = 0;

void noise() {
  // enable output on the two audio output pins

  DDRA |= 1 << PA6;
  DDRB |= 1 << PB2;

  // output frequency. turn on PA6 and PB2, wait for pitch microseconds
  // turn off PA6 and PB2, wait again for pitch microseconds.

  PORTA |= 1 << PA6;
  PORTB |= 1 << PB2;
  delayuss(pitch);

  PORTA &= ~(1 << PA6);
  PORTB &= ~(1 << PB2);
  delayuss(pitch);

  count--;
  if (count == 0) {
    count  = random() / 2000;
    pitch += sub;
  }

  if (pitch > 2000) {
    // pitch is going up by 50 (units?) each time count reaches zero.
    // if the pitch gets too high (meaning what? frequency too low), reset it
    // to something random.

    pitch = random() / 200;
  }

  // the original noisetoy checked the state of two buttons and made
  // random changes based on which were pressed. what could take the place
  // of the buttons? some degree of randomness.

  // disable output to audio output pins

  DDRA &= ~(1 << PA6);
  DDRB &= ~(1 << PB2);
}

void daveMatthewsBand() {
  // enable output on the two audio output pins

  DDRA |= 1 << PA6;
  DDRB |= 1 << PB2;

  // disable output to the two audio output pins

  DDRA &= ~(1 << PA6);
  DDRB &= ~(1 << PB2);

}

//
//''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

//''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
// slm() -- Sound and Light Machine
//   slm()
//   blinkLEDs()
//   doBrainwaveElement()
//

struct brainwaveElement {
  char bwType;  // 'a' for Alpha, 'b' for Beta, 't' for Theta, or 'd' for Delta ('0' signifies last entry in table
  unsigned long int bwDuration;  // Duration of this Brainwave Type (divide by 10,000 to get seconds)
} const brainwaveTab[] PROGMEM = {
  { 'b',  600000 }, { 'a',  100000 }, { 'b',  200000 }, { 'a',  150000 }, { 'b',  150000 },
  { 'a',  200000 }, { 'b',  100000 }, { 'a',  300000 }, { 'b',   50000 }, { 'a',  600000 },
  { 't',  100000 }, { 'a',  300000 }, { 't',  200000 }, { 'a',  300000 }, { 't',  300000 },
  { 'a',  150000 }, { 't',  600000 }, { 'a',  150000 }, { 'b',   10000 }, { 'a',  150000 },
  { 't', 1200000 }, { 'd',  600000 }, { 't', 1200000 }, { 'd',  600000 }, { 't', 1200000 },
  { 'd',  600000 }, { 't',  300000 }, { 'a',  150000 }, { 'b',   10000 }, { 'a',  150000 },
  { 't',  300000 }, { 'a',  150000 }, { 'b',   10000 }, { 'a',  200000 }, { 'b',   50000 },
  { 'a',  200000 }, { 'b',  150000 }, { 'a',  150000 }, { 'b',  200000 }, { 'a',  100000 },
  { 'b',  250000 }, { 'a',   50000 }, { 'b',  600000 }, { '0',       0 }
};

void blinkLEDs(
    unsigned long int duration, unsigned long int onTime,
    unsigned long int offTime) {
  int i;
  for (i = 0; i < duration / (onTime + offTime); i++) {
    // leds for breadboard
    // PORTA |= (1 << PA5) | (1 << PA4);     // turn on LEDs at PA5 and PA4, don't touch others
    // PORTB |= (1 << PB1) | (1 << PB0);     // turn on LEDs at PB1 and PB0, don't touch others
    // delay_one_tenth_ms(onTime);           //   for time length onTime
    // PORTA &= ~((1 << PA5) | (1 << PA4));  // turn off LEDs at PA5 and PA4, don't touch others
    // PORTB &= ~((1 << PB1) | (1 << PB0));  // turn off LEDs at PB1 and PB0, don't touch others
    // delay_one_tenth_ms(offTime);          //   for time length offTime

    // leds for tin
    // PORTA |= (1 << PA2) | (1 << PA1);     // turn on LEDs at PA2 and PA1, don't touch others
    // PORTB |= (1 << PB1) | (1 << PB0);     // turn on LEDs at PB1 and PB0, don't touch others
    // delay_one_tenth_ms(onTime);           //   for time length onTime
    // PORTA &= ~((1 << PA2) | (1 << PA1));  // turn off LEDs at PA2 and PA1, don't touch others
    // PORTB &= ~((1 << PB1) | (1 << PB0));  // turn off LEDs at PB1 and PB0, don't touch others
    // delay_one_tenth_ms(offTime);          //   for time length offTime

    // blinking the leds in the tin causes an oscillation in frequency not caused by the
    // two different frequencies. this does not show up on the breadboard. turn of the lights
    // but keep the delay function, otherwise something buzzes in one of the outputs.

    delay_one_tenth_ms(onTime);           //   for time length onTime
    delay_one_tenth_ms(offTime);          //   for time length offTime

    // test to see if this loop should be terminated. when the program is changed, the
    // loop terminates. in the case of the slm(), the timers for audio need to be turned
    // off as well.

    if (programShouldStopRunning) { break; }
  }
}

void doBrainwaveElement(int index) {
  char brainChr = pgm_read_byte(&brainwaveTab[index].bwType);
  if (brainChr == 'b') { // beta
    OCR1A = 18628;  // T1 generates 415.024Hz, for a binaural beat of 14.4Hz
    blinkLEDs(pgm_read_dword(&brainwaveTab[index].bwDuration), 347, 347);
  }
  else if (brainChr == 'a') { // alpha
    OCR1A = 18919;  // T1 generates 411.734Hz, for a binaural beat of 11.1Hz
    blinkLEDs(pgm_read_dword(&brainwaveTab[index].bwDuration), 451, 450);
  }
  else if (brainChr == 't') { // theta
    OCR1A = 19386;  // T1 generates 406.628Hz, for a binaural beat of 6.0Hz
    blinkLEDs(pgm_read_dword(&brainwaveTab[index].bwDuration), 835, 835);
  }
  else if (brainChr == 'd') { // delta
    OCR1A = 19750;  // T1 generates 402.860Hz, for a binaural beat of 2.2Hz
    blinkLEDs(pgm_read_dword(&brainwaveTab[index].bwDuration), 2253, 2253);
  }
  else {
    // this should never be executed, since we catch the end of table in the main loop
  }
}

void slm() {
  TIMSK0 = 0x00;  // timer/counter 0 interrupt mask register; no timer interrupts enabled
  TIMSK1 = 0x00;  // timer/counter 1 interrupt mask register; no timer interrupts enabled

  // enable output on the two audio output pins

  DDRA |= 1 << PA6;
  DDRB |= 1 << PB2;

  PORTA = 0x00;
  PORTB = 0x00;  // all PORTB output pins Off

  // for a complete explanation of these settings, see the documentation in the
  // original slm file. it is a very valuable explanation of what all of these
  // settings do.

  TCCR0A = B8(01000010);
  TCCR0B = B8(00000100);
  OCR0A = 77;

  TCCR1A = B8(01000000);
  TCCR1B = B8(00001001);
  TCCR1C = B8(00000000);

  // loop through entire array of brainwave elements

  int i = 0;
  while (pgm_read_byte(&brainwaveTab[i].bwType) != '0') {  // '0' signifies end of table
    doBrainwaveElement(i);
    i++;

    if (programShouldStopRunning) {
      programShouldStopRunning = false;

      // just turn everything related to the timers off. this should reset the pins to
      // their default state.

      TCCR0A = B8(00000000);  // CS02:CS00=000 to stop Timer0 (turn off audio in Right ear speaker)
      TCCR0B = B8(00000000);  // CS12:CS10=000 to stop Timer1 (turn off audio in Left ear speaker)

      TCCR1A = B8(00000000);  // CS12:CS10=000 to stop Timer1 (turn off audio in Left ear speaker)
      TCCR1B = B8(00000000);  // CS12:CS10=000 to stop Timer1 (turn off audio in Left ear speaker)
      TCCR1C = B8(00000000);  // CS12:CS10=000 to stop Timer1 (turn off audio in Left ear speaker)

      // disable output to audio output pins

      DDRA &= ~(1 << PA6);
      DDRB &= ~(1 << PB2);

      break;
    }
  }
}

//
//''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

//''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
// miniPOV()
//

const char string_0[] PROGMEM = "M A P";
const char string_1[] PROGMEM = "BLINKENLIGHTS";
const char string_2[] PROGMEM = "FUKENGRUVEN";
const char string_3[] PROGMEM = "MPS";
const char string_4[] PROGMEM = "MAKE STUFF";
const char string_5[] PROGMEM = "HANNAH";
const char string_6[] PROGMEM = "OBJECTS OF VIRTUE";
const char string_7[] PROGMEM = "HAPPY HANUKKAH";

#define bit_get(p,m) ((p) & (m))
#define bit_set(p,m) ((p) |= (m))
#define bit_clear(p,m) ((p) &= ~(m))
#define bit_flip(p,m) ((p) ^= (m))
#define bit_write(c,p,m) (c ? bit_set(p,m) : bit_clear(p,m))
#define BIT(x) (0x01 << (x))
#define LONGBIT(x) ((unsigned long)0x00000001 << (x))

const uint8_t characters[] PROGMEM = { // PROGMEM
  B8(01111110), B8(00010001), B8(00010001), B8(00010001), B8(01111110), B8(00000000), // A
  B8(01111111), B8(01001001), B8(01001001), B8(01001001), B8(00110110), B8(00000000), // B
  B8(00111110), B8(01000001), B8(01000001), B8(01000001), B8(00100010), B8(00000000), // C
  B8(01111111), B8(01000001), B8(01000001), B8(00100010), B8(00011100), B8(00000000), // D
  B8(01111111), B8(01001001), B8(01001001), B8(01001001), B8(01000001), B8(00000000), // E
  B8(01111111), B8(00001001), B8(00001001), B8(00001001), B8(00000001), B8(00000000), // F
  B8(01111110), B8(01000001), B8(01001001), B8(01001001), B8(01111010), B8(00000000), // G
  B8(01111111), B8(00001000), B8(00001000), B8(00001000), B8(01111111), B8(00000000), // H
  B8(01000001), B8(01111111), B8(01000001), B8(00000000), B8(00000000), B8(00000000), // I
  B8(00100000), B8(01000000), B8(01000001), B8(00111111), B8(00000001), B8(00000000), // J
  B8(01111111), B8(00001000), B8(00010100), B8(00100010), B8(01000001), B8(00000000), // K
  B8(01111111), B8(01000000), B8(01000000), B8(01000000), B8(01000000), B8(00000000), // L
  B8(01111111), B8(00000010), B8(00001100), B8(00000010), B8(01111111), B8(00000000), // M
  B8(01111111), B8(00000100), B8(00001000), B8(00010000), B8(01111111), B8(00000000), // N
  B8(00111110), B8(01000001), B8(01000001), B8(01000001), B8(00111110), B8(00000000), // O
  B8(01111111), B8(00001001), B8(00001001), B8(00001001), B8(00000110), B8(00000000), // P
  B8(00111110), B8(01000001), B8(01010001), B8(00100001), B8(01011110), B8(00000000), // Q
  B8(01111111), B8(00001001), B8(00011001), B8(00101001), B8(01000110), B8(00000000), // R
  B8(01000110), B8(01001001), B8(01001001), B8(01001001), B8(00110001), B8(00000000), // S
  B8(00000001), B8(00000001), B8(01111111), B8(00000001), B8(00000001), B8(00000000), // T
  B8(00111111), B8(01000000), B8(01000000), B8(01000000), B8(00111111), B8(00000000), // U
  B8(00011111), B8(00100000), B8(01000000), B8(00100000), B8(00011111), B8(00000000), // V
  B8(00111111), B8(01000000), B8(00111000), B8(01000000), B8(00111111), B8(00000000), // W
  B8(01100011), B8(00010100), B8(00001000), B8(00010100), B8(01100011), B8(00000000), // X
  B8(00000111), B8(00001000), B8(01110000), B8(00001000), B8(00000111), B8(00000000), // Y
  B8(01100001), B8(01010001), B8(01001001), B8(01000101), B8(01000011), B8(00000000), // Z
  B8(00000000), B8(00000000), B8(00000000), B8(00000000), B8(00000000), B8(00000000)  // space
};

PGM_P p;
char buffer[25];

PGM_P stringSet[] PROGMEM = {
  string_0, string_1, string_2, string_3,
  string_4, string_5, string_6, string_7
};

void miniPOV() {
  PGM_P characters_p = characters;
  memcpy_P(&p, &stringSet[state], sizeof(PGM_P));
  strcpy_P(buffer, p);

  // load the string into buffer and then create an array of chars that
  // contains all of the pieces of the letters. iterate over that
  // array to show the phrase.

  int index, i = 0, k;
  char letter;
  while (buffer[i] != '\0') {
    if (buffer[i] == ' ') index = 26;
    else index = buffer[i] - 'A';

    for (k = 0; k < 6; k++) {
      letter = pgm_read_byte(characters_p + 6*index + k);

      /*
      // sequence of leds on the breadboard
      bit_write(bit_get(letter, BIT(0)), PORTA, BIT(5)); // PB1
      bit_write(bit_get(letter, BIT(1)), PORTA, BIT(4)); // PB0
      bit_write(bit_get(letter, BIT(2)), PORTA, BIT(3)); // PA5
      bit_write(bit_get(letter, BIT(3)), PORTA, BIT(2)); // PA4
      bit_write(bit_get(letter, BIT(4)), PORTA, BIT(1)); // PA3
      bit_write(bit_get(letter, BIT(5)), PORTA, BIT(0)); // PA0
      bit_write(bit_get(letter, BIT(6)), PORTB, BIT(0)); // PA1
      bit_write(bit_get(letter, BIT(7)), PORTB, BIT(1)); // PA2
      */

      // sequence of leds in the tin
      bit_write(bit_get(letter, BIT(0)), PORTB, BIT(1)); // PB1
      bit_write(bit_get(letter, BIT(1)), PORTB, BIT(0)); // PB0
      bit_write(bit_get(letter, BIT(2)), PORTA, BIT(5)); // PA5
      bit_write(bit_get(letter, BIT(3)), PORTA, BIT(4)); // PA4
      bit_write(bit_get(letter, BIT(4)), PORTA, BIT(3)); // PA3
      bit_write(bit_get(letter, BIT(5)), PORTA, BIT(0)); // PA0
      bit_write(bit_get(letter, BIT(6)), PORTA, BIT(1)); // PA1
      bit_write(bit_get(letter, BIT(7)), PORTA, BIT(2)); // PA2


      delay_ms(2);

      bit_clear(PORTA, B8(00111111));
      bit_clear(PORTB, B8(00000011));
    }

    i++;
  }

  // something needs to be done with the timing. should the compare match
  // interrupt be used (as in minipov) or is a slight delay using delay_ms()
  // enough?

  // this would be improved by reading the word once and creating an array of
  // chars that contain the leds to light up. it would also then make it easier to
  // use a timer interrupt for better control.

  delay_ms(10);
}

//
//''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

//''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
// miniPOV()
//

uint8_t PA, PB;

// uint8_t portAData[8] = {34U, 50U, 58U, 62U, 63U, 63U, 63U, 63U};
// uint8_t portBData[8] = { 0U,  0U,  0U,  0U,  0U,  1U,  3U, 11U};

/*
bbba1aaa5  000100010  00000000
bbba1aa45  000110010  00000000
bbba1a345  000111010  00000000
bbba12345  000111110  00000000
bbb012345  000111111  00000000
bb0012345  000111111  00000001
b10012345  000111111  00000011
310012345  000111111  00001011
*/

uint8_t portAData[8] = {16U, 16U, 16U, 48U, 56U, 57U, 59U, 63U};
uint8_t portBData[8] = { 0U,  2U,  3U,  3U,  3U,  3U,  3U,  3U};

/*
aaaaaabbb
aaaa4abb3  00010000  00001000  16  8  0
aaaa4ab13  00010000  00001010  16 10  2
aaaa4a013  00010000  00001011  16 11  3
aaaa45013  00110000  00001011  48 11  3
aaa345013  00111000  00001011  56 11  3
aa0345013  00111001  00001011  57 11  3
a10345013  00111011  00001011  59 11  3
210345013  00111111  00001011  63 11  3
*/

void menorah() {
  PA = portAData[state];
  PB = portBData[state];

  uint8_t n = 0;
  while (n < 8U) {
    PORTA |= PA & (1 << n);
    PORTB |= PB & (1 << n);

    uint8_t i = 0;
    while (i < 7U) {
      asm("nop");
      i++;
    }

    PORTA &= ~(PA & (1 << n));
    PORTB &= ~(PB & (1 << n));

    n++;
  }
}

//
//''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
